# -*- coding: utf-8 -*-

'''
Created on 26/11/2011

@author: Luis M. Ugalde
www.forondarena.net

Copyright © 2011 Luis M. Ugalde
Licensed under the terms of the MIT License
(see LICENSE file for details)

This software can't be considered to be even in Alpha state. It still needs a lot of work.

Remember that abstract interfaces have a different syntax in python 3:
 You need the: from abc...
                class AbstractReader(metaclass = ABCMeta)
 and you dont need the __metaclass__ ...
'''


from abc import abstractmethod, ABCMeta
import zmq
from writers.writerexception import WriterException


class AbstractWriter(object):
    '''
    Generic class to be inherited by every writer implementation.
    Writers must implement all defined methods. Raise TypeError otherwise
    '''


    __metaclass__ = ABCMeta


    def __init__(self, *args, **kwargs):
        self.context = zmq.Context()
        self.poller = zmq.Poller()
        self.statestoreconnectaddresses = []
        self.statestoreconnectsockets = []
        self.statestoreconnectnotifyaddresses = []
        self.statestoreconnectnotifysocket = None


    def setSource(self, proto = "inproc", host = "statestore", port = None):
        '''
        The source for a writer is always a zeromq socket created by a State Store.
        '''

        port = str(port)
        self.statestoreconnectaddresses.append(proto + '://' + host + ':' + port if port else proto + '://' + host)
        print "{}: StateStore socket is {}".format(self.__class__.__name__, proto + '://' + host + ':' + port if port else proto + '://' + host) 


    def connectSources(self):
        '''
        Use pull sockets to read data from State Stores.
        '''
        
        try:
            i = 0
            while i < len(self.statestoreconnectaddresses):
                print "Connecting  to {}.".format(self.statestoreconnectaddresses[i])
                tmpsocket = self.context.socket(zmq.PULL)
                tmpsocket.connect(self.statestoreconnectaddresses[i])
                self.statestoreconnectsockets.append(tmpsocket)
                self.poller.register(self.statestoreconnectsockets[i], zmq.POLLIN)
                i += 1
        except zmq.ZMQError:
            if __debug__:
                print "Could not connect to state store sources. Trying to close everything."
            self.closeSources()
            raise WriterException('Could not connect to statestore sources.')


    def closeSources(self):
        '''
        Close the open parser socket.
        '''
        
        for i in self.statestoreconnectsockets:
            self.poller.unregister(i)
            i.setsockopt(zmq.LINGER, 0)
            i.close()


    def setNotifySource(self, proto = "inproc", host = "notifyparser", port = None):
        '''
        Set up a socket to receive broadcast from available parsers.
        '''

        port = str(port)
        self.statestoreconnectnotifyaddresses.append(proto + '://' + host + ':' + port if port else proto + '://' + host)
        print "{}: Parser notify socket is {}".format(self.__class__.__name__, proto + '://' + host + ':' + port if port else proto + '://' + host)
        
        
    def connectNotifySources(self):
        '''
        Connect the previously defined sources to receive broadcasts from readers.
        '''
        
        try:
            for i in self.statestoreconnectnotifyaddresses:
                print "Connecting  to {}.".format(i)
                self.statestoreconnectnotifysocket = self.context.socket(zmq.SUB)
                self.statestoreconnectnotifysocket.connect(i)
        
            self.statestoreconnectnotifysocket.setsockopt(zmq.SUBSCRIBE, "")
            self.poller.register(self.statestoreconnectnotifysocket, zmq.POLLIN)
        except zmq.ZMQError:
            if __debug__: print "Could not connect to state store source notify sockets. Trying to close everything."
            self.closeNotifySources()
            raise WriterException('Could not connect to state store notify sources.')


    def closeNotifySources(self):
        '''
        Close the open parser socket.
        '''

        self.poller.unregister(self.statestoreconnectnotifysocket)        
        self.statestoreconnectnotifysocket.setsockopt(zmq.LINGER, 0)
        self.statestoreconnectnotifysocket.close()


    @abstractmethod
    def setBindWriter(self, *args, **kwargs):
        '''
        Set the destination for the received records.
        '''
        pass


    @abstractmethod        
    def bindWriter(self, *args, **kwargs):
        '''
        For a writer, binding may mean to open a text file, or to connect to a database, solr, cassandra, ...
        '''
        pass


    @abstractmethod
    def closeBindWriter(self):
        pass


    @abstractmethod
    def putRecords(self, *args, **kwargs):
        pass


    def closeContext(self):
        self.context.term()


if __name__ == '__main__':
    print "Please, do not run this"
